chore: release 2.3.1#608
Conversation
fix: use core LNURL-pay validation
Co-authored-by: Cursor <cursoragent@cursor.com>
Greptile SummaryThis hotfix routes LNURL-pay invoice fetching through
Confidence Score: 3/5The core LNURL validation logic is solid, but the QuickPay flow silently discards the new validation errors, leaving users stranded on the loading screen without any feedback when an invoice fails validation. The central improvement — delegating invoice fetch and validation to bitkit-core — is clean and the error mapping in Lnurl.swift is correct. The problem is in SendQuickpay.swift: validation errors thrown by fetchLnurlInvoice propagate out of performPayment() and are silently discarded by the unstructured Task in onAppear. This is the exact scenario the PR is meant to improve (invalid invoices from LNURL servers), so the failure to surface those errors in the QuickPay path undermines the intent of the fix. SendQuickpay.swift needs the fetch-error path handled before the inner do-catch; LnurlPayConfirm.swift should consistently catch fetchLnurlInvoice errors alongside send errors.
|
| Filename | Overview |
|---|---|
| Bitkit/Utilities/Lnurl.swift | Replaces ad-hoc HTTP invoice fetch with bitkit-core's getLnurlInvoiceForPayData; adds LnurlPayInvoiceMismatchError and mapLnurlPayInvoiceError to translate validation failures into a neutral user-facing message. Logic is sound. |
| Bitkit/Views/Wallets/Send/SendQuickpay.swift | Updated call site passes LnurlPayData instead of callbackUrl. The unstructured Task in onAppear swallows any error thrown by fetchLnurlInvoice before the inner do-catch, leaving the user stuck on the loading screen when core validation fails. |
| Bitkit/Views/Wallets/Send/LnurlPayConfirm.swift | Updated call site passes LnurlPayData. fetchLnurlInvoice errors propagate outside performPayment's inner do-catch, so their toast+navigation path relies on SwipeButton handling them gracefully. |
| Bitkit/ViewModels/Trezor/TrezorViewModel.swift | Adds WatcherError case to the error description switch to match a new variant introduced in bitkit-core 0.1.74. Straightforward and correct. |
| Bitkit.xcodeproj/project.pbxproj | Bumps CURRENT_PROJECT_VERSION to 191 and MARKETING_VERSION to 2.3.1 across all six build targets (Debug/Release x app, widget, notification). Consistent and correct. |
| Bitkit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | bitkit-core pinned from 0.1.64 (a7577cc) to 0.1.74 (201e37b). Lock file updated to match the project.pbxproj requirement. |
| CHANGELOG.md | Adds 2.3.1 release entry and updates the Unreleased comparison link. Follows Keep a Changelog conventions correctly. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant UI as SendQuickpay / LnurlPayConfirm
participant Helper as LnurlHelper
participant Core as BitkitCore
participant Server as LNURL Server
UI->>Helper: fetchLnurlInvoice(data, amountMsats)
Helper->>Core: getLnurlInvoiceForPayData(data, amountMsats)
Core->>Server: "HTTP GET callback?amount=..."
Server-->>Core: bolt11 invoice
Core->>Core: Validate amount / metadata
alt Validation passes
Core-->>Helper: invoice string
Helper-->>UI: invoice string
UI->>UI: sendWithTimeout(bolt11)
else Validation fails
Core-->>Helper: throws LnurlError
Helper->>Helper: mapLnurlPayInvoiceError
Helper-->>UI: throws LnurlPayInvoiceMismatchError
note over UI: SendQuickpay: swallowed by Task, no UX feedback
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant UI as SendQuickpay / LnurlPayConfirm
participant Helper as LnurlHelper
participant Core as BitkitCore
participant Server as LNURL Server
UI->>Helper: fetchLnurlInvoice(data, amountMsats)
Helper->>Core: getLnurlInvoiceForPayData(data, amountMsats)
Core->>Server: "HTTP GET callback?amount=..."
Server-->>Core: bolt11 invoice
Core->>Core: Validate amount / metadata
alt Validation passes
Core-->>Helper: invoice string
Helper-->>UI: invoice string
UI->>UI: sendWithTimeout(bolt11)
else Validation fails
Core-->>Helper: throws LnurlError
Helper->>Helper: mapLnurlPayInvoiceError
Helper-->>UI: throws LnurlPayInvoiceMismatchError
note over UI: SendQuickpay: swallowed by Task, no UX feedback
end
Comments Outside Diff (1)
-
Bitkit/Views/Wallets/Send/SendQuickpay.swift, line 36-39 (link)Unstructured Task silently swallows LNURL validation errors
Task { try await performPayment() }drops any error thrown before the innerdo-catchis reached. With this PR,fetchLnurlInvoicenow performs amount/metadata validation via core and can throwLnurlPayInvoiceMismatchError; that error propagates out ofperformPayment()but is silently discarded by the unstructuredTask. The user is left stranded on the QuickPay loading spinner with no feedback. The network and parse errors that existed before this PR had the same fate, but this PR makes the path far more likely to be hit (e.g. any server returning an amount that doesn't match). Wrapping thefetchLnurlInvoicecall in ado-catchthat mirrors the inner error handler (toast + navigate to.failure) would close the gap.
Reviews (1): Last reviewed commit: "chore: version 2.3.1" | Re-trigger Greptile
| let bolt11 = try await LnurlHelper.fetchLnurlInvoice( | ||
| callbackUrl: lnurlPayData.callback, | ||
| data: lnurlPayData, | ||
| amountMsats: amountMsats, | ||
| comment: comment.isEmpty ? nil : comment | ||
| ) |
There was a problem hiding this comment.
Invoice-fetch errors escape the inner catch block
fetchLnurlInvoice is called before the do-catch that toasts the error and navigates to .failure. If core validation throws (e.g. AmountMismatch), the error propagates out of performPayment() to SwipeButton's closure; whether SwipeButton surfaces it to the user is not guaranteed. The inner do-catch already handles send failures with a toast + failure navigation — fetch failures should be treated the same way for consistent UX.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d700f60075
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| data: lnurlPayData, | ||
| amountMsats: lnurlPayData.callbackAmountMsats() |
There was a problem hiding this comment.
Handle LNURL validation failures in QuickPay
When a fixed-amount LNURL-pay endpoint returns an invoice that fails the new core amount/metadata validation, this call now throws before performPayment() reaches the do/catch that navigates to the failure state. In QuickPay it is invoked from Task { try await performPayment() } without any outer catch, so the payment is correctly cancelled but the user remains on the loader indefinitely instead of seeing the failure path; wrap the fetch/parse step in the existing error handling or catch at the task boundary.
Useful? React with 👍 / 👎.
fix: update bitkit core
Collect hotfix changelog for #610 and bump build for TestFlight upload. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Hotfix release 2.3.1 (build 192) on top of v2.3.0.
Description
LnurlPayDatavalidationCURRENT_PROJECT_VERSION: 191 → 192 (build-only bump for TestFlight; marketing version unchanged)MARKETING_VERSION: 2.3.0 → 2.3.1Release
ovi/release-2.3.0Screenshot / Video
N/A
QA Notes
@lnurl_1green with matching e2e branch (fix/lnurl-pay@d00494d)